package org.zjt.file;

import sun.misc.Cleaner;

import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.AccessController;
import java.security.PrivilegedAction;

/**
 * Description: 小文件读取
 * <p>
 * 1、Mmap在小内存文件读取优于filechannel
 * 2、MMAP 为什么在一次写入少量数据的场景下表现的比 FileChannel 优异
 * 3、顺序写、文件切分 可以保证文件下入的冲突（offset 顺序）。
 * 4、当需要使用的内存很大时候，需要
 *
 * @author juntao.zhang
 * Date: 2018-11-28 1:40 PM
 * @see
 */
public class MmapFileOperator {

    public static void main(String[] args) throws Exception {
        RandomAccessFile memoryMappedFile = new RandomAccessFile("/Users/juntao.zhang/dev/workspace/myproject/java-basic/src/main/java/org/zjt/nio/MmapOperator1.txt", "rw");


        // Mapping a file into memory
        MappedByteBuffer out = memoryMappedFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 1024);


        // Writing into Memory Mapped File
        for (int i = 0; i < 1024; i++) {
            out.put((byte) 'A');
        }
        System.out.println("Writing to Memory Mapped File is completed");


        // reading from memory file in Java
        for (int i = 0; i < 10; i++) {
            System.out.print((char) out.get(i));
        }
        System.out.println("Reading from Memory Mapped File is completed");


        memoryMappedFile.close();

        //刷新出文件。并清理mmap buffer。
        out.force();
        clean(out);
    }


    /**
     * MappedByteBuffer 的内存释放（直接内存）
     * @param mappedByteBuffer
     * @throws Exception
     */
    public static void clean(final MappedByteBuffer mappedByteBuffer) throws Exception {

        if (mappedByteBuffer == null || !mappedByteBuffer.isDirect() || mappedByteBuffer.capacity() == 0){
            return;
        }
        AccessController.doPrivileged((PrivilegedAction) () -> {
            try {
                Method getCleanerMethod = mappedByteBuffer.getClass().getMethod("cleaner",new Class[0]);
                getCleanerMethod.setAccessible(true);
                Cleaner cleaner =(Cleaner)getCleanerMethod.invoke(mappedByteBuffer,new Object[0]);
                cleaner.clean();
            } catch(Exception e) {
                e.printStackTrace();
            }
            return null;});

    }
}
